The Javascript string length is not suitable for measuring the
approximate length of a rendered text in the input method
indicator. Most single code point emoji like `๐ U+1F680` get length 2
already, adding a variation selector to make the emoji display in
black and white like `๐๏ธ U+1F680 U+FE0E` makes the length 3. More
complicated emoji sequences may get much longer lengths even though
the still display as one character. For example `๐ฉโ๐ฉโ๐งโ๐ง U+1F469,
U+200D, U+1F469, U+200D, U+1F467, U+200D, U+1F467` gets length 11.
This is not only the case for emoji but also for other text, for
example the Korean jamo `แแ
กแจ U+1100, U+1161, U+11A8` display as only 1
glyph but the Javascript length is 3.
A better way to measure the approximate length of a rendered text is
to count the Unicode grapheme clusters in the text using
Intl.Segmenter. This counts all the examples above as 1 grapheme
cluster each.
There seem to be some bugs in the Intl.Segmenter, for example
`เคเฅเคทเคฟ U+0915, U+094D, U+0937, U+093F` is counted as 2 grapheme clusters,
the correct result should be 1. But it is good enough to measure
the approximate length of the input method indicator and certainly
better than the Javascript string length.
Resolves: https://gitlab.gnome.org/GNOME/gnome-shell/-/issues/1026
Part-of: <https://gitlab.gnome.org/GNOME/gnome-shell/-/merge_requests/3753>
}
}
+ _getGraphemeClusters(text = '') {
+ const segmenter = new Intl.Segmenter(undefined, {granularity: 'grapheme'});
+ const segments = [...segmenter.segment(text)].map(o => o.segment);
+ return segments;
+ }
+
_buildPropSubMenu(menu, props) {
if (!props)
return;
let currentSource = this._inputSourceManager.currentSource;
if (currentSource) {
let indicatorLabel = this._indicatorLabels[currentSource.index];
- if (text && text.length > 0 && text.length < 3)
+ const graphemeClusters = this._getGraphemeClusters(text);
+ if (graphemeClusters.length > 0 && graphemeClusters.length < 3)
indicatorLabel.set_text(text);
}
}